#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Author : gao
# Time : 2020/7/4 18:25

import numpy as np

'''
    海伦约会k近邻
    归一化
'''


"""
Parameters:
    filename - 文件名
Returns:
    returnMat - 特征矩阵
    classLabelVector - 分类Label向量
"""
# 函数说明:打开并解析文件，对数据进行分类：1代表不喜欢,2代表魅力一般,3代表极具魅力
def file2matrix(filename):
    #读取文件
    with open(filename) as f:
        #按行读取
        lines=f.readlines()
        #返回的数据矩阵
        dataMat = np.zeros((len(lines),3))
        #返回的标签列表
        labelsVerctor = []

        numLine = 0  #遍历行
        #遍历所有行
        for line in lines:
            #去掉每一行的首尾空白字母
            line = line.strip()
            listFromLine = line.split('\t')
            #把每行的前三个数据写进矩阵
            dataMat[numLine] =listFromLine[0:3]
            #每行的第四个写进标签列表中
            labelsVerctor.append(listFromLine[3])

            numLine+=1
    return dataMat,labelsVerctor

'''
    归一化处理
    dataSet:要处理的数据
    返回归一化得矩阵
'''
def norm(dataSet):
    #按列找出最大最小值，生成两个向量
    minVal=np.min(dataSet,axis=0)
    maxVal=np.max(dataSet,axis=0)
    #最大最小的差值向量
    mdiff = maxVal-minVal
    #数据矩阵得行数
    row=dataSet.shape[0]
    #数据集与最小值的差值
    dataSet = dataSet - np.tile(minVal,(row,1)) #得先把向量复制成矩阵，在做减法
    #归一化
    normMat = dataSet / np.tile(mdiff,(row,1))
    return normMat,mdiff,minVal



# 函数说明:kNN算法,分类器
def classify0(inX, dataSet, labels, k):
    # 把inx复制为与dataSet同维数
    inX = np.tile(inX, (dataSet.shape[0], 1))
    # 做平方差
    diffMat = (inX - dataSet) ** 2
    # 将第一维度相加，（维度从0开始）
    lossMat = np.sum(diffMat, axis=1)
    # 排序
    sortedTags = np.argsort(lossMat)
    # 定义一个字典，保存每个的类别的数量
    dictTags = {}
    for i in range(k):
        # 获取第i小的 的标签
        tmpTag = labels[sortedTags[i]]
        # 把标签放进字典
        dictTags[tmpTag] = dictTags.get(tmpTag, 0) + 1  # 相当于java的map的getOrDefault()
    # 找最大的标签
    minTimes = -1  # 擂台法
    closest = 0  # 记录标签
    for key in dictTags.keys():
        if dictTags[key] > minTimes:
            minTimes = dictTags[key]
            closest = key
    return closest

'''
    测试模型得正确率
'''
def datingClassTest():
    #打开的文件名
    filename = r'C:\Users\gao\Desktop\Machine-Learning-in-Action-master\Machine-Learning-in-Action-master\机器学习实战数据集\Ch02-KNN\datingTestSet2.txt'
    #将返回的特征矩阵和分类向量分别存储到datingDataMat和datingLabels中
    datingDataMat, datingLabels = file2matrix(filename)
    #取所有数据的百分之十,其余作为样本去求近邻
    hoRatio = 0.10
    #数据归一化,返回归一化后的矩阵,数据范围,数据最小值
    normMat, ranges, minVals = norm(datingDataMat)
    #获得normMat的行数
    m = normMat.shape[0]
    #百分之十的测试数据的个数
    numTestVecs = int(m * hoRatio)
    #分类错误计数
    errorCount = 0.0

    for i in range(numTestVecs):
        #前numTestVecs个数据作为测试集,后m-numTestVecs个数据作为训练集
        classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],
            datingLabels[numTestVecs:m], 4)
        print("分类结果:%s\t真实类别:%s" % (classifierResult, datingLabels[i]),end=' ')
        if classifierResult != datingLabels[i]:
            errorCount += 1.0
            print('错误')
        else:
            print('')
    print("错误率:%.2f%%" %(errorCount/float(numTestVecs)*100))



if __name__ == '__main__':
    datingClassTest()